package org.ejde.sciencelogs;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

/**
 * This class contains the attributes and behaviors for iniParser. iniParser
 * parses an XML-based configuration file and provides access methods for
 * retrieving information from the XML file.
 * 
 * @author Eric Martin
 * @version 0.1
 */
public class iniParser {

	/**
	 * Global document object.
	 */
	private static Document doc;

	// Other Essential Gathered Information
	/**
	 * Number of panels
	 */
	public int Npanels;

	/**
	 * Title of the Application
	 */
	public String Title;

	/**
	 * Constructor method. Parses the supplied an XML based on the supplied
	 * filename
	 * 
	 * @param xmlfile
	 *            filename of the XML-based configuration file.
	 */
	public iniParser(String xmlfile) {
		String fname = xmlfile;

		// System.out.println("Parsing");

		// Create a new XML document builder factory
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

		// factory.setValidating(true);
		// factory.setNamespaceAware(true);

		// Parse the supplied XML file
		try {

			DocumentBuilder builder = factory.newDocumentBuilder();
			File inifile;

			// Check to see if the filename is actually a
			// URL (starts with "http://").
			if (fname.startsWith("http://")) {
				// URI uriname = new URI(fname);
				// inifile = new File(uriname);
				doc = builder.parse(fname);
			}
			// Otherwise, treat the configuration file as a
			// file on the local filesystem
			else {
				inifile = new File(fname);
				doc = builder.parse(inifile);
			}

			// Begin Gathering Information

			// Count the number of panels
			Npanels = this.panelCount();

			// Get the application title information
			Title = getLoggerTitle();

			// Parser Output Check
			// System.out.println("Loaded XML File for: "+Title);
			// for (int i = 0;i<Npanels;i++){
			// System.out.println("Panel: "+getPanelAttribute(i,"name"));
			// int j = getFieldCount(i);
			// System.out.println("	Fields: "+j);
			// }

		} catch (SAXException sxe) {
			// Error generated during parsing)
			Exception x = sxe;
			if (sxe.getException() != null)
				x = sxe.getException();
			x.printStackTrace();

		} catch (ParserConfigurationException pce) {
			// Parser with specified options can't be built
			pce.printStackTrace();

		} catch (IOException ioe) {
			// I/O error
			ioe.printStackTrace();
		}
	}

	/**
	 * Get method. Return the requested attribute of a panel.
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @param name
	 *            the name of the attribute
	 * @return the value of the attirbute as a string
	 */
	public String getPanelAttribute(int panelID, String name) {
		Element elem = getPanel(panelID);
		String attr = elem.getAttributes().getNamedItem(name).getNodeValue();
		return attr;
	}

	/**
	 * Get method. Return the requested attribute of a subpanel.
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @param subpanelID
	 *            the ID of the subpanel to query
	 * @param name
	 *            the name of the attribute
	 * @return the value of the attirbute as a string
	 */
	public String getSubpanelAttribute(int panelID, int subpanelID, String name) {
		String attr = null;
		Node node = null;

		Element elem = getSubpanel(panelID, subpanelID);
		node = elem.getAttributes().getNamedItem(name);
		if (node != null) {
			attr = node.getNodeValue();
		}
		return attr;
	}

	/**
	 * Get method. Returns the requested attribute of a field.
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @param subpanelID
	 *            the ID of the subpanel to query
	 * @param fieldID
	 *            the ID of the fieldpanel to query
	 * @param name
	 *            the name of the attribute
	 * @return the value of the attirbute as a string
	 */
	public String getFieldAttribute(int panelID, int subpanelID, int fieldID,
			String name) {
		String attr = null;
		Node node = null;
		if (panelID == -1) {
			// Superfield
			node = doc.getElementsByTagName("field").item(fieldID)
					.getAttributes().getNamedItem(name);
		} else if (panelID != -1 & subpanelID == -1) {
			// Field
			Element panel = this.getPanel(panelID);
			node = panel.getElementsByTagName("field").item(fieldID)
					.getAttributes().getNamedItem(name);
		} else if (panelID != -1 & subpanelID != -1) {
			// Subfield
			Element subpanel = this.getSubpanel(panelID, subpanelID);
			node = subpanel.getElementsByTagName("field").item(fieldID)
					.getAttributes().getNamedItem(name);
		}
		if (node != null) {
			attr = node.getNodeValue();
		}

		return attr;
	}

	/**
	 * Get Method. Return the title of the application extracted from the XML
	 * file
	 * 
	 * @return the application title as a string
	 */
	public String getLoggerTitle() {
		String title;
		title = doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("title").getNodeValue();
		return title;
	}

	/**
	 * Get method. Return the logger category
	 * 
	 * @return the category as a string
	 */
	public String getLoggerCategory() {
		String cat;
		cat = doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("category").getNodeValue();
		return cat;
	}

	/**
	 * Get method. Return the logfile delimiter
	 * 
	 * @return the logfile delimiter as a string
	 */
	public String getLoggerDelimiter() {
		String dlm;
		dlm = doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("delimiter").getNodeValue();
		if (dlm.equals("\\t")) {
			dlm = "\t";
		} else if (dlm.equals("\\n")) {
			dlm = "\n";
		} else if (dlm.equals("\\r")) {
			dlm = "\r";
		} else if (dlm.equals("\\n\\r")) {
			dlm = "\n\r";
		} else if (dlm.equals("\\r\\n")) {
			dlm = "\r\n";
		}
		return dlm;
	}

	// Private Functions
	/**
	 * Get method. Return a panel as an element
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @return the panel as an element
	 */
	private Element getPanel(int panelID) {
		Element panel = (Element) doc.getElementsByTagName("panel").item(
				panelID);
		return panel;
	}

	/**
	 * Get method. Return a subpanel as an element
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @param subpanelID
	 *            the ID of the subpanel to query
	 * @return the subpanel as an element
	 */
	private Element getSubpanel(int panelID, int subpanelID) {
		Element panel = (Element) this.getPanel(panelID);
		Element subpanel = (Element) panel.getElementsByTagName("subpanel")
				.item(subpanelID);
		return subpanel;
	}

	/**
	 * Get method. Return a field as an element
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @param subpanelID
	 *            the ID of the subpanel to query
	 * @param fieldID
	 *            the ID of the field to query
	 * @return the field as an element
	 */
	public Element getField(int panelID, int subpanelID, int fieldID) {
		Element field = null;

		// Default value.
		field = (Element) doc.getElementsByTagName("field").item(fieldID);

		if (panelID == -1) {
			// Superfield
			field = (Element) doc.getElementsByTagName("field").item(fieldID);
		} else if (panelID != -1 & subpanelID == -1) {
			// Field
			Element panel = this.getPanel(panelID);
			field = (Element) panel.getElementsByTagName("field").item(fieldID);
		} else if (panelID != -1 & subpanelID != -1) {
			// Subfield
			Element subpanel = this.getSubpanel(panelID, subpanelID);
			field = (Element) subpanel.getElementsByTagName("field").item(
					fieldID);
		}

		return field;
	}

	/**
	 * Get method. Return the options of a field as an array of strings
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @param subpanelID
	 *            the ID of the subpanel to query
	 * @param fieldID
	 *            the ID of the field to query
	 * @return the options as an array of strings
	 */
	public String[] getOptions(int panelID, int subpanelID, int fieldID) {
		String[] options = null;
		Element field = this.getField(panelID, subpanelID, fieldID);
		int Noptions = getChildCount(field, "option");
		if (Noptions != 0) {
			options = new String[Noptions];
			for (int n = 0; n < Noptions; n++) {
				Element opt = (Element) field.getElementsByTagName("option")
						.item(n);
				options[n] = this.getText(opt);
				if (options[n] == null) {
					options[n] = " ";
				}
			}
		}
		return options;
	}

	// Counting Functions
	/**
	 * Count the number of panels
	 * 
	 * @return the number of panels
	 */
	public int panelCount() {
		int count = doc.getElementsByTagName("panel").getLength();
		return count;
	}

	/**
	 * Count the number of subpanels with in a panel
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @return the number of subpanels
	 */
	public int subpanelCount(int panelID) {
		Element panel = this.getPanel(panelID);
		int count = panel.getElementsByTagName("subpanel").getLength();
		return count;
	}

	/**
	 * Get method. Return the number of fields
	 * 
	 * @return the number of fields
	 */
	public int getFieldCount() {
		int count = doc.getElementsByTagName("field").getLength();
		return count;
	}

	/**
	 * Get method. Return the number of child elements based on a tag name.
	 * 
	 * NEED HELP UNDERSTANDING THIS ONE
	 * 
	 * @param name
	 *            the tag name to query
	 * @return the number of child elements based on a string name.
	 */
	public int getFieldCount(String name) {
		// To be a valid xml, only 1 scilog tag should occur
		Element scilog = (Element) doc.getElementsByTagName(name).item(0);
		int count = this.getChildCount(scilog, "field");
		// System.out.println("superfield count: "+count);
		return count;
	}

	/**
	 * Get method. Return the number of child elements based on a panelID.
	 * 
	 * NEED HELP UNDERSTANDING THIS ONE
	 * 
	 * @param panelID
	 *            the ID of the panel to query
	 * @return the number of child elements for the specified panel.
	 */
	public int getFieldCount(int panelID) {
		Element panel = this.getPanel(panelID);
		int ct = this.getChildCount(panel, "field");
		// System.out.println("field count: "+ct);
		// int count = panel.getElementsByTagName("field").getLength();
		return ct;
	}

	/**
	 * Get method. Return the number of fields in a subpanel.
	 * 
	 * @param panelID
	 *            the panel ID to query.
	 * @param subpanelID
	 *            the subpanel ID to query.
	 * 
	 * @return the number of fields in the subpanel.
	 */
	public int getFieldCount(int panelID, int subpanelID) {
		Element panel = this.getSubpanel(panelID, subpanelID);
		int ct = this.getChildCount(panel, "field");
		// System.out.println("subfield count: "+ct);
		// int count = panel.getElementsByTagName("field").getLength();
		return ct;
	}

	/*
	 * public int getSuperfieldCount() { int count =
	 * doc.getElementsByTagName("superfield").getLength(); return count; }
	 */
	/*
	 * 
	 * public int getSubfieldCount(int panelID,int subpanelID) { Element
	 * subpanel = this.getSubpanel(panelID, subpanelID); int count =
	 * subpanel.getElementsByTagName("subfield").getLength(); return count; }
	 */
	/*
	 * public int getSuperfieldCountofType(String type) { int N =
	 * getSuperfieldCount(); int count = 0; for (int n=0;n<N;n++) { if
	 * (this.getSuperfield(n).getAttribute("type").equals(type)) { count++; } }
	 * return count; }
	 */
	/*
	 * public int getFieldCountofType(int panelID, String type) { int N =
	 * getFieldCount(panelID); int count = 0; for (int n=0;n<N;n++) { if
	 * (this.getField(panelID,n).getAttribute("type").equals(type)) { count++; }
	 * } return count; }
	 */
	/*
	 * public int getSubfieldCountofType(int panelID, int subpanelID, String
	 * type) { int N = getSubfieldCount(panelID,subpanelID);
	 * 
	 * System.out.println("subfields:  "+N); int count = 0; for (int
	 * n=0;n<N;n++) { if
	 * (this.getSubfield(panelID,subpanelID,n).getAttribute("type"
	 * ).equals(type)) { count++; } } return count; }
	 */

	/**
	 * Return the number of child elements that have the specified name.
	 * 
	 * @param elem
	 *            the element to search
	 * @param ename
	 *            the name to query
	 * @return the number of child elements with the specified name
	 */
	public int getChildCount(Element elem, String ename) {
		int N = elem.getChildNodes().getLength();

		int Nelement = 0;
		for (int n = 0; n < N; n++) {
			Node node = elem.getChildNodes().item(n);
			if (node.getNodeType() == Node.ELEMENT_NODE
					& node.getNodeName() == ename) {
				Nelement++;
			}
		}
		return Nelement;
	}

	/**
	 * Return the text of an element as a string.
	 * 
	 * @param elem
	 *            the element to query.
	 * @return the element's content as a string.
	 */
	public String getText(Element elem) {
		String output = null;
		int N = elem.getChildNodes().getLength();

		for (int n = 0; n < N; n++) {
			Node node = elem.getChildNodes().item(n);
			if (node.getNodeType() == Node.TEXT_NODE) {
				output = node.getNodeValue().trim();
				// System.out.println("got string: "+output);
			}
		}
		return output;
	}

	/**
	 * Get method. Return the IP address of the UDP server as a string.
	 * 
	 * @return the IP address of the UDP server as a string.
	 */
	public String getDestIP() {
		String targetIP;
		targetIP = doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("udpdestip").getNodeValue();
		return targetIP;
	}

	/**
	 * Get method. Return the port number of the UDP server as a int.
	 * 
	 * @return the IP address of the UDP server as an int.
	 */
	public int getDestPort() {
		String targetPort;
		int port;

		targetPort = doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("udpdestport").getNodeValue();
		port = Integer.parseInt(targetPort);
		return port;
	}

	/**
	 * Get method. Return the port number of the local log as an int.
	 * 
	 * @return the port number of the local log as an int.
	 */
	public int getLocalLog() {
		String targetPort;
		int port;

		targetPort = doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("locallog").getNodeValue();
		port = Integer.parseInt(targetPort);
		return port;
	}

	/**
	 * Set the locallog flag.
	 * 
	 * @param locallogflag
	 *            the locallog flag.
	 */
	public void setLocalLog(int locallogflag) {
		String flag = Integer.toString(locallogflag);
		doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("locallog").setNodeValue(flag);

	}

	/**
	 * Get method. Return the local log filename as a string.
	 * 
	 * @return the local log filename.
	 */
	public String getLocalLogFile() {
		String locallogfile;

		locallogfile = doc.getElementsByTagName("scilog").item(0)
				.getAttributes().getNamedItem("locallogfile").getNodeValue();

		return locallogfile;
	}

	/**
	 * Set the local log filename.
	 * 
	 * @param locallogfile
	 *            the filename of the local log.
	 */
	public void setLocalLogFile(String locallogfile) {
		doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("locallogfile").setNodeValue(locallogfile);
	}

	/**
	 * Get method. Return the XMPPEnabled flag. NEED TO SPECIFY WHAT'S TRUE AND
	 * WHAT'S FALSE
	 * 
	 * @return the XMPPEnabled flag as an int.
	 */
	public int getXmppEnabled() {
		String xmppEnabled;
		int xmppenabled;

		xmppEnabled = doc.getElementsByTagName("scilog").item(0)
				.getAttributes().getNamedItem("xmppenabled").getNodeValue();
		xmppenabled = Integer.parseInt(xmppEnabled);
		return xmppenabled;
	}

	/**
	 * Set the XMPPEnabled flag. NEED TO SPECIFY WHAT'S TRUE AND WHAT'S FALSE
	 * 
	 * @param xmppenabled
	 *            the XMPPEnabled flag
	 */
	public void setXmppEnabled(int xmppenabled) {
		String flag = Integer.toString(xmppenabled);
		doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("xmppenabled").setNodeValue(flag);

	}

	/**
	 * Get Method. Returns the XMPP server name/IP as a string.
	 * 
	 * @return the XMPP server name/IP as a string.
	 */
	public String getXmppServer() {
		String xmppServer;

		xmppServer = doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("xmppserver").getNodeValue();

		return xmppServer;
	}

	/**
	 * Set the XMPP server name/IP.
	 * 
	 * @param xmppServer
	 *            the XMPP server name/IP.
	 */
	public void setXmppServer(String xmppServer) {
		doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("xmppserver").setNodeValue(xmppServer);
	}

	/**
	 * Get method. Return the XMPP server port number as an int.
	 * 
	 * @return the XMPP server number as a int.
	 */
	public int getXmppServerPort() {
		String xmppServerPort;
		int xmppserverport;

		xmppServerPort = doc.getElementsByTagName("scilog").item(0)
				.getAttributes().getNamedItem("xmppserverport").getNodeValue();
		xmppserverport = Integer.parseInt(xmppServerPort);
		return xmppserverport;
	}

	/**
	 * Set the XMPP server port number.
	 * 
	 * @param xmppserverport
	 *            the XMPP server port number.
	 */
	public void setXmppServerPort(int xmppserverport) {
		String xmppServerPort = Integer.toString(xmppserverport);
		doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("xmppserverport").setNodeValue(xmppServerPort);
	}

	/**
	 * Get method. Return the number of the XMPP MUC room.
	 * 
	 * @return the name of the XMPP MUC room.
	 */
	public String getXmppTargetRoom() {
		String xmppTargetRoom;

		xmppTargetRoom = doc.getElementsByTagName("scilog").item(0)
				.getAttributes().getNamedItem("xmpptargetroom").getNodeValue();

		return xmppTargetRoom;
	}

	/**
	 * Set the name of the XMPP MUC room.
	 * 
	 * @param xmppTargetRoom
	 *            the name of the XMPP MUC room.
	 */
	public void setXmppTargetRoom(String xmppTargetRoom) {
		doc.getElementsByTagName("scilog").item(0).getAttributes()
				.getNamedItem("xmpptargetroom").setNodeValue(xmppTargetRoom);
	}
}